package org.oep.widgets;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import java.util.Vector;
import java.util.Hashtable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.game.GameCanvas;

import java.io.InputStream;
import java.io.IOException;

public class MonologueWidget implements Runnable {
	private Font font = Font.getDefaultFont();
	private int x, y;
	private int width, height;
	private int rows;
	private int margin;
	private boolean active = false;
	
	private Vector fullText = new Vector();  // All the dialogue
	private Vector rawBuffer = new Vector();
	
	private Hashtable substitutions;
	private int tick = 1;
	
	private int currentLine = 0;
	private int currentBlock = 0;
	
	private Thread blinkThread;
	
	private boolean waitForAction = false;
	private boolean continueBlink = false;
	private boolean graphicsFlushed = false;
	private Image orb;
	
	public MonologueWidget(int x, int y, int width, int rows, int margin) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.rows = rows;
		this.margin = margin;
		
		height = rows * font.getHeight() + 2 * margin; 
		
		try {
			orb = Image.createImage("/res/graphics/orb.png");
		}
		catch(IOException e) {
			
		}
	}
	
	public void loadFile(String filename) {
		if(substitutions == null)
			substitutions = new Hashtable();
		fullText.removeAllElements();
		rawBuffer.removeAllElements();
		try {
			// Load the file into a buffer
			InputStream is = getClass().getResourceAsStream(filename);
			StringBuffer linesBuffer = new StringBuffer();
			int ch;
			while((ch = is.read()) != -1) {
					linesBuffer.append((char)ch);
			}
			//System.out.println(linesBuffer.toString());
			
			// After the lines are loaded, perform any substitutions
			String s = linesBuffer.toString();
			while(s.indexOf('%') >= 0) {
				int a = s.indexOf('%');
				int b = s.indexOf('%', a+1);
				if(b < 0) break; // No matching '%'
				String key = s.substring(a+1, b);
				String val = (String) substitutions.get(key);
				if(val == null) val = "(null)";
				s = s.substring(0, a) + val + s.substring(b+1, s.length());
			}
			
			
			addToBuffer(s);
		}
		catch(IOException e) {
			System.err.println(e);
		}
		// Add in the line breaks
	}
	
	public void display(String msg) {
		addToBuffer(msg);
	}
	
	public void clearBuffer() {
		currentLine = 0;
		currentBlock = 0;
		tick = 1;
		rawBuffer.removeAllElements();
		fullText.removeAllElements();
	}
	
	public void run() {
		// Blink the orb
		while(true) {
			continueBlink = !continueBlink;
			try {
				Thread.sleep(500);
			}
			catch(Exception e) {}
		}
	}
	
	private void addToBuffer(String s) {
		// Find the line breaks and add them to the raw buffer
		String [] lines = Utils.tokenize(s,'\n');
		for(int i = 0; i < lines.length; i++) {
			rawBuffer.addElement(lines[i].trim());
		}
		formatBuffer();
	}
	
	private void formatBuffer() {
		fullText.removeAllElements();
		for(int j = 0; j < rawBuffer.size(); j++) {
			String line = new String();
			Vector lines = new Vector();
			String [] tokens = Utils.tokenize((String) rawBuffer.elementAt(j));
			for(int i = 0; i < tokens.length; i++) {
				String word = tokens[i];
				
				
				int lineWidth = font.charsWidth(line.toCharArray(), 0, line.length());
				int wordWidth = font.charsWidth(word.toCharArray(), 0, word.length());
				
								
				// Since the word is so long, display on one line
				if( wordWidth > width - 2 * margin) {
					if(line.length() > 0) {
						lines.addElement(line);
						line = "";
					}
					lines.addElement(word);
				} else if((lineWidth + wordWidth) > width - 2 * margin) {
					lines.addElement(line);
					line = word + " ";
				} else {
					line += word + " ";
				}
	
			}
			if(line.length() > 0) {
				lines.addElement(line);
			}
			
			// After we've created our buffer, add it to the big one
			fullText.addElement(lines);
		}
	}
	
	public void tick() {
		if(!active) return;
		
		// Check the thread
		if(blinkThread == null || !blinkThread.isAlive()) {
			if(blinkThread == null)
				blinkThread = new Thread(this);
			blinkThread.start();
		}
		
		Vector cur = (Vector) fullText.elementAt(currentBlock);
		int length = ((String)cur.elementAt(currentLine)).length();
		if(tick < length)
			tick++;
		else if(currentLine < cur.size() - 1) {
			currentLine++;
			tick = 0;
		} else {
			waitForAction = true;
		}
			
	}
	
	public void action() {
		if(!active) return;
		Vector cur = (Vector) fullText.elementAt(currentBlock);
		
		if(!waitForAction) {
			currentLine = cur.size() - 1;
			tick = ((String) cur.lastElement()).length();
			waitForAction = true;
		} else {
			waitForAction = false;
			tick = 0;
			currentLine = 0;
			currentBlock++;
			if(currentBlock >= fullText.size())
				active = false;
		}
	}
	
	public void paint(Graphics g) {
		if(!active) {
			return;
		}
		
		// We've painted, so we'll need to flush the graphics
		graphicsFlushed = false;
		
		Font f = g.getFont();
		g.setFont(font);
		g.setColor(0xFFFFFF);
		g.fillRect(x, y, width, height);
		g.setColor(0);
		g.drawRect(x, y, width, height);
		
		Vector cur = (Vector) fullText.elementAt(currentBlock);
		
		if(cur.size() > 0) {
			// We're drawing from the bottom up
			int anticipatedHeight = font.getHeight() * Math.min(rows - 1, currentLine + 1);
			int offset = (height / 2) + (anticipatedHeight / 2) + margin;
			String partialLine = (String) cur.elementAt(currentLine);
			
			if(tick < partialLine.length())
				partialLine = partialLine.substring(0, tick);

			// Begin the drawing
			g.drawString(partialLine, x + margin, y + offset, Graphics.BOTTOM | Graphics.LEFT);
			offset -= font.getHeight();
			for(int i = 1; i < Math.min(rows, currentLine + 1); i++) {
				String s = (String) cur.elementAt(currentLine - i);
				g.drawString(s, x + margin, y + offset, Graphics.BOTTOM | Graphics.LEFT);
				offset -= font.getHeight();
			}
		}
		
		if(continueBlink && waitForAction) {
			if(orb != null)
				g.drawImage(orb, x + width - 1, y + height - 1, Graphics.RIGHT|Graphics.BOTTOM);
			else
				g.drawString(">>", x + width - 1, y + height - 1, Graphics.RIGHT|Graphics.BOTTOM);
		}
		g.setFont(f);
	}
	
	public void setSubTable(Hashtable t) {
		substitutions = t;
	}
	
	public void setActive(boolean b) {
		active = b;
	}
	
	public boolean isActive() {
		return active;
	}
	
	public int getX() { return x; }
	public int getY() { return y; }
	public int getWidth() { return width; }
	public int getHeight() { return height; }
	
	// This will flush the portion of the screen monoBox belongs to
	public void flushGraphics(GameCanvas c) {
		if(!graphicsFlushed) {
			c.flushGraphics(x, y, width + 1, height + 1);
			graphicsFlushed = true;
			c.repaint();
		}
	}
	
	public void setFont(Font f) {
		font = f;
		formatBuffer();
	}

}
